<!doctype html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport"
          content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>Document</title>
</head>
<body>
<ul>
    <li></li>
    <li></li>
    <li></li>
</ul>
<script>
    /*
    * 面向对象：
    *   1.介绍函数和对象之间的关系
    *   2.原型和原型链
    *   3.对象的继承方法
    *
    *   1.什么是函数？函数有什么用？
    *       作用1：整合封装代码，让调用更加清晰
    *       作用2：用来生成对象
    *
    *   任何对象都是由函数生成的，这种生成对象的函数是构造函数
    *       1.构造函数 和 普通函数(function)有什么区别？
    *           没什么区别，只是使用方式的区别。普通函数直接调用foo();构造函数 new foo()调用
    *
    *       2.构造函数 是 怎么生成对象的？
    *           通过 new 函数() 的方式 就可以生成对象
    *
    *   约定俗成的共识：构造函数首字母大写 function Foo(){}
    *
    *   万物皆对象：不管写出任何的数据类型，都是由构造函数生成(除了undefined和null是例外,这两个是由开发js的那门语言规定的)
    *
    *
    *
    * */

    let obj1 = {}
    //这个对象obj1看起来不是构造函数生成的，其实是构造函数生成的
    //包装对象和字面量 {}其实是一个语法糖(很好用人人都喜欢用) 这里的大括号{}就是 new Object() 的简写

    let num = 3
    //万物皆对象，这个3也属于对象，而对象都是由构造函数生成的，那么这个数字3其实也是由构造函数生成的
    //3其实就是 new Number(3)


    /*
    * 构造函数非常厉害，可以生成对象(实例对象)
    *
    * 其中，构造函数是前辈，实例对象是后辈
    *       血肉相连 => 基因相连
    *
    * 构造函数在生成对象的时候可以对 对象进行一些设置
    *   即在构造函数中设置this就直接影响生成的对象
    *
    * */
    function Foo(){ //构造函数
       this.name = "后辈" //构造函数的this可以影响到实例出来的对象
    }
    Foo.a = 1 //给构造函数里面添加一个a属性，值为1
    let obj = new Foo() //这里等号后面的 构造函数new Foo() 产生了等号前面的 对象obj
    console.log(obj) //这里的obj对象会有a属性等于1吗？ 其实并不会，因为没有涉及到"基因"，但是会有name属性，值为后辈

    /*
    * 既然血肉相连就是基因相连，那么对象的"基因"是什么？
    *
    * 数组 类数组
    *
    * 对象访问属性是怎么访问的？
    *   1.看对象内部有没有这个方法，如果有就直接返回执行这个方法
    *   2.如果没有这个方法，就去原型当中寻找
    *
    *
    *   任何对象都有一个__proto__的属性，这个属性就是对象的"基因"，我们把这条属性称为隐式原型
    *
    *   这个隐式原型是从哪里来的？
    *   constructor 构造者 构造器
    *
    *   任何函数都有一条属性 .prototype 它是这个函数在实例对象的时候 传递的基因
    *
    *   构造函数的 .prototype === 实例对象的 .__proto__ (两者完全相等，是同一个东西，因为对象是引用类型，持有同一个引用地址)
    *   解释：构造函数在实例化的时候，会将构造函数自己内部的一条属性 .prototype也就是构造函数的原型，传递给实例对象，
    *       传递到实例对象的 .__proto__属性当中，构造函数的prototype是什么样子，它所实例出来的对象就应该是什么样子
    *
    * */


    //例举一个例子看看：这个隐式原型是从哪里来的？
    function Foo1(){}
    Foo1.prototype.say = "hello" //构造函数Foo1的prototype属性添加一个say方法，值为hello，由于对象是引用类型的关系
    //无论这个方法是在实例对象之前添加还是实例对象之后再添加到构造函数Foo1的prototype属性上，实例出来的实例对象objfoo都会有say方法
    let objfoo = new Foo1() //构造函数Foo1实例对象objfoo
    console.log(objfoo)
    console.log(objfoo.constructor) //打印objfoo对象的构造者 会返回Foo1函数
    console.log(objfoo.say) //由构造函数Foo1实例出来的对象也会有这样一个say方法

    //用一个构造函数Foo2，生成3000个实例对象，将这3000个实例对象全部添加进一个数组arr1中，如何给这3000个实例对象添加一条属性state，
    //属性值为"交流过了"
    let arr1 = []
    let Foo2 = function (){}
    for(let i = 0;i < 3000;i++){
        arr1.push(new Foo2()) //实例对象，将实例对象添加进数组arr1
    }

    Foo2.prototype.state = "交流过了" //给构造函数Foo2的prototype属性添加state属性，值为交流过了


    /*
    * 为什么一个是数组，一个不是数组？
    * 看这两个东西是由什么构造函数生成的
    *
    * 什么是真正的数组？
    *   真正的数组：是由Array构造函数实例出来的，才是数组
    *
    * */
    let arr = [1,2,3] //数组
    let liList = document.querySelectorAll("li") //类数组 长的像数组，但是不是真的数组，没有数组的方法

    console.log(arr)
    console.log(liList)
    console.log(arr.constructor) //构造者是Array，Array.prototype有数组的所有方法
    console.log(liList.constructor) //构造者是NodeList，NodeList.prototype却没有数组方法

    //如果我们自己封装了一个myReduce方法，怎样给所有的数组添加上我们封装的这个方法
    Array.prototype.myReduce = function (){
        console.log("myReduce 执行")
    }

    //给任意一个对象添加一个说话的功能，说"银时牛逼"
    Object.prototype.say = function (){
        console.log("银时牛逼")
    }

</script>
</body>
</html>